// Copyright Leong Zhin Sheng

package cluster.networks.ib;

import src.*;
import cluster.simple_modules.*;

network clos648
{
	parameters:
		// it is suggested that the numMachine should be a multiple of the number of lowest level switch
		int numMachine = default(8); // the num of machines should be n^2/2 (where n is the num of lowest level switch)

		// it is suggested that the number of switch for each level should be a multiple of it's corresponding upper level switch 
		int numL1switch = default(4);
		int numL2switch = default(int(numL1switch/2));
		int numGpusperMachine = default(8);
		int switch_delay = default(0);
	
	submodules:
		machine[numMachine]: Machine 
		{
		    parameters:
		        @display("i=device/server2;p=80,109");
		}
	    
		taskgenerator: TaskGenerator
		{
		    parameters:
		        maxnumGpus = numGpusperMachine * numMachine;
		}

		centralcontroller: CentralController 
		{
		    parameters:
		        numMachine = numMachine;
		        numSwitch = numL1switch + numL2switch;
		        numGpusperMachine = numGpusperMachine;
		    gates:
		        machine_port[numMachine];
		        switch_port[numL1switch + numL2switch];
		}

		topo_manager : TopoManager 
		{

		}

		sw_l1[numL1switch]: IBSwitch 
		{
			parameters:
				numSwitchPorts = int(numMachine/numL1switch)*numGpusperMachine + numL2switch;
				cross_connected_upperlevel_numswitch = numL2switch;
				is_cross_connected = true;
				
				@display("p=182,58");
			gates:
				port[int(numMachine/numL1switch)*numGpusperMachine + numL2switch];
		}

		sw_l2[numL2switch]: IBSwitch 
		{
			parameters:
				numSwitchPorts = numL1switch;
				@display("p=182,58");
			gates:
				port[numL1switch];
		}

		virtual_switch: IB_Virtual_Data_Switch 
		{
		    parameters:
		        numSwitchPorts = numMachine*numGpusperMachine;
		    gates:
		        port[numMachine*numGpusperMachine];
        	}

	connections allowunconnected:
	
		// Connect the task generator and centralcontroller
        	taskgenerator.central_controller_port --> centralcontroller.task_generator_port;
	
		// Connect every machines to its respective L1 switch, central controller and virtual data switch
		for j = 0..numMachine-1
		{
		    machine[j].controller_port <--> ControlPlaneLink <--> centralcontroller.machine_port[j];
		}

		// Connect the machines to L1 switches and virtual data switch
		for i=0..numMachine-1, for j=0..numGpusperMachine-1 
		{
		    virtual_switch.port[i*numGpusperMachine+j] <--> ControlPlaneLink <-->machine[i].virtual_data_switch_port[j];
		    machine[i].out_port[j] <--> IB4XQDRWire <--> sw_l1[int(i/int(numMachine/numL1switch))].port[(j+i*numGpusperMachine) % (int(numMachine/numL1switch)*numGpusperMachine)];
		}
	
		// Connect the L1 switches to the central controller
		for i = 0..numL1switch-1
		{
		    sw_l1[i].central_controller_port <--> ControlPlaneLink <--> centralcontroller.switch_port[i];
		}
	
		// Connect the L2 switches to the central controller
		for i = 0..numL2switch-1
		{
		    sw_l2[i].central_controller_port <--> ControlPlaneLink <--> centralcontroller.switch_port[i+numL1switch];
		}
	
		// Connect the L1 switches to the L2 switches
		for i = 0..numL1switch-1, for j =0..numL2switch-1
		{
			sw_l1[i].port[j + int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[j].port[i];
		}
}